package camid.services.cloudmanager;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;

import camid.services.nameservice.EntityMetadata;

public class AdaptiveLoadBalancingStrategy implements LoadBalancingStrategy 
{
	
	private IClusterManagerRegistry clusterRegistry;
	
	private Random random;
	
	public AdaptiveLoadBalancingStrategy(IClusterManagerRegistry clusterRegistry) {
		super();
		this.clusterRegistry = clusterRegistry;
		random = new Random();
	}

	private Map<String,Double> getCostMap(List<String> nodeIds)
	{
		Map<String,Double> costMap = null;
//		costMap = new TreeMap<String, Double> ();
//		for(String nodeId : nodeIds)
//		{
//			Double cost = clusterRegistry.getNodeCost(nodeId);
//			cost = (cost == null || cost.equals(Double.NaN)) ? 0.0 : cost;
//			if(cost != null )
//			{
//				costMap.put(nodeId, cost);
//			}
//		}
		costMap = this.clusterRegistry.getCostMap(nodeIds);
		return costMap;
	}
	
	private String chooseRandomNode(Map<String,Double> costMap)
	{
		String chosenNode = null;
		Double leastCost = Double.MAX_VALUE;
		String leastCostNodeId = null;
		
		List<String> filledNodes = new ArrayList<String> ();
		for(Entry<String, Double> entry : costMap.entrySet())
		{
			Double cost = entry.getValue();
			if(cost > 0.0)
			{
				filledNodes.add(entry.getKey());
				if(cost < leastCost)
				{
					leastCostNodeId = entry.getKey();
					leastCost = cost;
				}
			}
		}
		
		if(filledNodes.size() >= 1)
		{
			return leastCostNodeId;
		}
		else
		{
			int index = this.random.nextInt(costMap.size());
			int i = 0;
			for(String node : costMap.keySet())
			{
				if( i == index )
				{
					return node;
				}
				i++;
			}
		}
		
		return chosenNode;
	}
	
	private String chooseLeastLoadedNode(Map<String, Double> costMap, String oldNodeId)
	{
		Double leastCost = Double.MAX_VALUE;
		String leastCostNodeId = null;
		
		Double oldNodeCost = costMap.get(oldNodeId);
		if(oldNodeCost == null || oldNodeCost == Double.NaN)
			oldNodeCost = 0.0;
		
		if( oldNodeCost < 0.6 )
		{
			return oldNodeId;
		}
		
		for(Entry<String,Double> entry : costMap.entrySet() )
		{
			Double cost = entry.getValue();
			if(cost < leastCost)
			{
				leastCost = cost;
				leastCostNodeId = entry.getKey();
			}
		}
		
		
		return leastCostNodeId;
	}
	
	private Double sumCostMap(Map<String,Double> costMap)
	{
		Double costSum = 0.0;
		
		for(Entry<String,Double> entry : costMap.entrySet())
		{
			costSum += entry.getValue();
		}
		
		return costSum;
	}
	
	
	@Override
	public String selectNode(EntityMetadata metadata, List<String> nodeIds) 
	{	
		String leastCostNodeId = null;
//		Double leastCost = Double.MAX_VALUE;
		
		Map<String, Double> costMap = getCostMap(nodeIds);
		
		leastCostNodeId = chooseRandomNode(costMap);
		leastCostNodeId = chooseLeastLoadedNode(costMap, leastCostNodeId);
		
//		for(String nodeId : nodeIds)
//		{
//			Double cost = clusterRegistry.getNodeCost(nodeId);
//			cost = (cost == null || cost.equals(Double.NaN)) ? 0.0 : cost;
//			if(cost != null && cost < leastCost)
//			{
//				leastCost = cost;
//				leastCostNodeId = nodeId;
//			}
//		}
		
		return leastCostNodeId;
	}

	@Override
	public String reselectNode(EntityMetadata metadata, List<String> nodeIds,
			String oldNodeId) 
	{
		String selectedNodeId = null;
		Map<String,Double> costMap = getCostMap(nodeIds);
		selectedNodeId = chooseLeastLoadedNode(costMap, oldNodeId);
		
//		if(metadata.getLifeCycleScope().equals(LifecycleScope.PER_REQUEST))
//		{
//			Map<String, Double> costMap = getCostMap(nodeIds);
//			selectedNodeId = chooseLeastLoadedNode(costMap, oldNodeId);
//		}
//		else
//		{
//			String leastCostNodeId = null;
//			Double leastCost = Double.MAX_VALUE;
//			Double oldNodeCost = 0.0;
//			

//			leastCostNodeId = chooseNode(costMap);
//			
//			oldNodeCost = costMap.get(oldNodeId);
//			if(oldNodeCost == null)
//			{
//				oldNodeCost = 0.0;
//			}
			
//			leastCost = costMap.get(leastCostNodeId);
//			
//			for(String nodeId : nodeIds)
//			{
//				Double cost = costMap.get(nodeId);
//				cost = (cost == null || cost.equals(Double.NaN)) ? 0.0 : cost;
//				if(oldNodeId.equals(nodeId))
//				{
//					oldNodeCost = cost;
//				}
//				if(cost != null && cost < leastCost)
//				{
//					leastCost = cost;
//					leastCostNodeId = nodeId;
//				}
//			}
//			if((oldNodeCost - leastCost) > 0.3)
//			{
//				selectedNodeId = leastCostNodeId;
//			}
//			else
//			{
//				selectedNodeId = oldNodeId;
//			}
//		}
		return selectedNodeId;
	}

	
	
}
